﻿using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;

namespace Jy.Abp.Theading;

/// <summary>
/// 并发控制器
/// </summary>
public class SemaphoreSlimManager : ISingletonDependency
{
    private static ConcurrentDictionary<string, Lazy<SemaphoreSlim>> _dicSemaphoreSlims = new ConcurrentDictionary<string, Lazy<SemaphoreSlim>>();

    private readonly IUnitOfWorkManager _unitOfWorkManager;

    public SemaphoreSlimManager(IUnitOfWorkManager unitOfWorkManager)
    {
        _unitOfWorkManager = unitOfWorkManager;
    }

    /// <summary>
    /// 获取一个指定key的并发对像，
    /// </summary>
    /// <param name="key">并发key,同一个key控制并发量，不同的key可单独控制</param>
    /// <param name="initialCount">The initial number of requests for the semaphore that can be granted concurrently</param>
    /// <param name="maximumCount">The maximum number of requests for the semaphore that can be granted concurrently</param>
    /// <returns></returns>
    public virtual SemaphoreSlim Get(string key = "default", int initialCount = 1, int maximumCount = 1)
    {
        var slim = _dicSemaphoreSlims.GetOrAdd(key, oid =>
        {
            return new Lazy<SemaphoreSlim>(() => new SemaphoreSlim(initialCount, maximumCount));
        }).Value;
        return slim;
    }
    /// <summary>
    /// 等待信号灯（用于处理并发排队，可指定key以便使用不同的信号灯）
    /// </summary>
    /// <param name="timeOut">超时时间（毫秒）</param>
    /// <param name="key">并发key,同一个key控制并发量，不同的key可单独控制</param>
    /// <param name="initialCount">The initial number of requests for the semaphore that can be granted concurrently</param>
    /// <param name="maximumCount">The maximum number of requests for the semaphore that can be granted concurrently</param>
    /// <returns>当前SemaphoreSlim对像</returns>
    public virtual async Task<SemaphoreSlim> WaitAsync(int timeOut = 5000, string key = "default", int initialCount = 1, int maximumCount = 1)
    {
        var slim = Get(key, initialCount, maximumCount);
        var enterSuccess = await slim.WaitAsync(timeOut);
        if (!enterSuccess)
            throw new UserFriendlyException("请求处理已超时，请稍后重试！");
        var currentUow = _unitOfWorkManager.Current;
        if (currentUow != null)
            currentUow.Disposed += (o, e) =>
            {
                if (slim.CurrentCount < maximumCount)
                    slim.Release();
            };
        return slim;
    }
    /// <summary>
    /// 等待信号灯（用于处理并发排队，可指定key以便使用不同的信号灯）
    /// </summary>
    /// <param name="timeOut">超时时间（毫秒）</param>
    /// <param name="key">并发key,同一个key控制并发量，不同的key可单独控制</param>
    /// <param name="initialCount">The initial number of requests for the semaphore that can be granted concurrently</param>
    /// <param name="maximumCount">The maximum number of requests for the semaphore that can be granted concurrently</param>
    /// <returns>当前SemaphoreSlim对像</returns>
    public virtual SemaphoreSlim Wait(int timeOut = 5000, string key = "default", int initialCount = 1, int maximumCount = 1)
    {
        var slim = Get(key, initialCount, maximumCount);
        var enterSuccess = slim.Wait(timeOut);
        if (!enterSuccess)
            throw new UserFriendlyException("请求处理已超时，请稍后重试！");
        var currentUow = _unitOfWorkManager.Current;
        if (currentUow != null)
            currentUow.Disposed += (o, e) =>
            {
                slim.Release();
            };
        return slim;
    }
}
